import { Box2, Mesh, PlaneGeometry, ShaderMaterial, Texture, Vector2, Vector3 } from 'three'
import { init, Rect, Group } from 'zrender'
import { ZRenderType } from 'zrender/lib/zrender'
import GeoTIFF, { fromBlob } from 'geotiff'
import axios from 'axios'
import { PROJECTION, TERRIANHOST, XTO1, YTO1 } from '../Util/static'
import { CqkjMap } from './CqkjMap'

const tempV3 = new Vector3()
const tempV2 = new Vector2()
export class Ground {
  readonly zr: ZRenderType
  readonly root = new Group({
    silent: true
  })

  readonly box = new Box2()
  readonly material: ShaderMaterial
  readonly mesh: Mesh<PlaneGeometry, ShaderMaterial>
  readonly key = new Vector3()
  readonly map: CqkjMap

  constructor(map: CqkjMap) {
    this.map = map
    this.zr = createZR()
    this.zr.add(this.root)
    this.material = createMaterial(this.zr, this.box)
    this.mesh = new Mesh(new PlaneGeometry(1, 1, 256, 256), this.material)
    this.mesh.layers.enable(1)
  }

  update() {
    const { map } = this
    const target = map.control.target
    const zoom = Math.round(map.zoom)
    const scale = Math.pow(2, zoom)
    const i = Math.round(XTO1.forward(target.x) * scale)
    const j = Math.round(YTO1.forward(target.y) * scale)
    this.setKey(i, j, zoom)
  }

  setKey(i: number, j: number, k: number) {
    tempV3.set(i, j, k)
    if (!this.key.equals(tempV3)) {
      const total = Math.pow(2, k)
      this.key.copy(tempV3)
      const { box, zr, mesh, root } = this
      box.min.set(XTO1.backward((i - 8) / total), YTO1.backward((j + 8) / total))
      box.max.set(XTO1.backward((i + 8) / total), YTO1.backward((j - 8) / total))
      const scaleX = 4096 / (box.max.x - box.min.x)
      const scaleY = 4096 / (box.min.y - box.max.y)
      const x = -box.min.x * scaleX
      const y = -box.max.y * scaleY
      root.attr({
        x, y, scaleX, scaleY
      })
      zr.refreshImmediately()
      const { offset, scale } = PROJECTION
      const step = box.getSize(tempV2).divideScalar(512)
      const min = box.min.clone().sub(step)
      const max = box.max.clone().add(step)
      if (TERRIANHOST) {
        const url = `${TERRIANHOST}?request=GetMap&service=WMS&format=image/tiff&crs=3857&bbox=${min.x + offset.x
          },${min.y + offset.y},${max.x + offset.x},${max.y + offset.y}&width=${257}&height=${257}`
        axios.get(url, {
          responseType: 'blob'
        }).then(async (res) => {
          const geometry = mesh.geometry
          const position: Float32Array = geometry.attributes.position.array as any
          if (res.data.type === 'image/tiff') {
            const geoTIFF: GeoTIFF = await fromBlob(res.data)
            const data = ((await geoTIFF.readRasters()) as any)[0] as Int16Array
            data.forEach((e, i) => {
              position[i * 3 + 2] = e === 32767 ? 0 : e * scale
            })
          } else {
            for (let i = 2; i < position.length; i += 3) {
              position[i] = 0
            }
          }
          geometry.attributes.position.needsUpdate = true
          mesh.scale.set(box.max.x - box.min.x, box.max.y - box.min.y, 1)
          mesh.position.set((box.max.x + box.min.x) / 2, (box.max.y + box.min.y) / 2, 0)
        })
      } else {
        mesh.scale.set(box.max.x - box.min.x, box.max.y - box.min.y, 1)
        mesh.position.set((box.max.x + box.min.x) / 2, (box.max.y + box.min.y) / 2, 0)
      }

    }
  }
}


function createZR() {
  const div = document.createElement('div')
  const zr = init(div, {
    devicePixelRatio: window.devicePixelRatio
  })
  
  const rect = new Rect({
    silent: true,
      z: 0,
      shape: {
        x: 0,
        y: 0,
        width: 4096,
        height: 4096
      },
      style: {
        fill: '#101912',
      }
  })
  zr.add(rect)
  zr.refreshImmediately()
  zr.resize({
    width: 4096,
    height: 4096
  })
  return zr
}

function createMaterial(zr: ZRenderType, box: Box2) {
  const canvas = zr.dom!.children[0].children[0] as HTMLCanvasElement
  const texture = new Texture(canvas)
  const refresh = zr.painter.refresh.bind(zr.painter)
  zr.painter.refresh = () => {
    refresh()
    texture.needsUpdate = true
  }
  return new ShaderMaterial({
    transparent: false,
    uniforms: {
      u_box_min: {
        value: box.min
      },
      u_box_max: {
        value: box.max
      },
      u_texture: {
        value: texture
      }
    },
    vertexShader: `
    varying vec2 v_uv;
    varying float v_h;
    uniform vec2 u_box_min;
    uniform vec2 u_box_max;
    
    void main(){
      vec4 mPosition=modelMatrix  * vec4(position,1.0);
      v_uv=(mPosition.xy/mPosition.w-u_box_min)/(u_box_max-u_box_min);
      v_h=mPosition.z/mPosition.w;
      gl_Position=projectionMatrix * viewMatrix * mPosition;
    }
    `,
    fragmentShader: `
    varying float v_h;
    varying vec2 v_uv;
    uniform sampler2D u_texture;

    vec4 mergeColor(vec4 c1,vec4 c2){
      if(c1.a==0.0)return c2;
      if(c2.a==0.0)return c1;
      float a=c1.a+c2.a-c1.a*c2.a;
      return vec4((c1.rgb*c1.a*(1.0-c2.a)+c2.rgb*c2.a)/a,a);
    }

    void main(){
      // gl_FragColor=texture2D(u_texture,v_uv);
      gl_FragColor= mergeColor(texture2D(u_texture,v_uv),max(0.0,sign(v_h-14000.0))*vec4(0.403921568627451,0.24705882352941178,0.20784313725490197, 0.2));
    }
    `
  })
}
